﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace User.Api.Helpers.WeixinPayV3
{
    public class WxPayHelper
    {
        private const string SignType = "SHA1";

        private readonly Dictionary<string, string> _parameters;
        private string _appid;
        private string _appkey;
        private string _mchid;
   


        public WxPayHelper(string appid,string appkey,string mchid)
        {
            _parameters = new Dictionary<string, string>();
            _appid = appid;
            _appkey = appkey;
            _mchid = mchid;
        }

        private void SetParameter(string key, string value)
        {
            _parameters.Add(key, value);
        }

        private string GetParameter(string key)
        {
            return _parameters[key];
        }

        private bool CheckCftParameters()
        {
            if (_parameters["bank_type"] == "" || _parameters["body"] == "" || _parameters["partner"] == "" || _parameters["out_trade_no"] == ""
                    || _parameters["total_fee"] == "" || _parameters["fee_type"] == "" || _parameters["notify_url"] == null || _parameters["spbill_create_ip"] == ""
                    || _parameters["input_charset"] == "")
            {
                return false;
            }
            return true;
        }

        private string GetCftPackage()
        {
            if ("" == _appkey)
            {
                throw new Exception("密钥不能为空！");
            }
            string unSignParaString = CommonUtil.FormatBizQueryParamMap(_parameters, false, true);
            string paraString = CommonUtil.FormatBizQueryParamMap(_parameters, true, true);
            return paraString + "&sign="
                    + MD5Util.MD5Sign(unSignParaString, _appkey);
        }

        public string GetBizSign(Dictionary<string, string> bizObj)
        {
            var bizParameters = new Dictionary<string, string>();

            foreach (KeyValuePair<string, string> item in bizObj)
            {
                if (item.Key != "")
                {
                    bizParameters.Add(item.Key.ToLower(), item.Value);
                }
            }

            if (_appkey == "")
            {
                throw new Exception("APPKEY为空！");
            }
            bizParameters.Add("appkey", _appkey);
            string bizString = CommonUtil.FormatBizQueryParamMap(bizParameters, false, true);

            return Sha1Utility.GetSha1(bizString);
        }

        // 生成app支付请求json
        /*
         * { "appid":"wwwwb4f85f3a797777", "traceid":"crestxu",
         * "noncestr":"111112222233333", "package":
         * "bank_type=WX&body=XXX&fee_type=1&input_charset=GBK&notify_url=http%3a%2f%2f
         * www
         * .qq.com&out_trade_no=16642817866003386000&partner=1900000109&spbill_create_ip
         * =127.0.0.1&total_fee=1&sign=BEEF37AD19575D92E191C1E4B1474CA9",
         * "timestamp":1381405298,
         * "app_signature":"53cca9d47b883bd4a5c85a9300df3da0cb48565c",
         * "sign_method":"sha1" }
         */
        private string CreateAppPackage(string traceid)
        {
            var nativeObj = new Dictionary<string, string>();
            if (CheckCftParameters() == false)
            {
                throw new Exception("生成package参数缺失！");
            }
            nativeObj.Add("appid", _appid);
            nativeObj.Add("package", GetCftPackage());
            nativeObj.Add("timestamp", ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString());
            nativeObj.Add("traceid", traceid);
            nativeObj.Add("noncestr", CommonUtil.CreateNoncestr(16));
            nativeObj.Add("app_signature", GetBizSign(nativeObj));
            nativeObj.Add("sign_method", SignType);

            var entries = nativeObj.Select(d => string.Format("\"{0}\": \"{1}\"", d.Key, d.Value));

            return "{" + string.Join(",", entries.ToArray()) + "}";
        }

        // 生成jsapi支付请求json
        /*
         * "appId" : "wxf8b4f85f3a794e77", //公众号名称，由商户传入 "timeStamp" : "189026618",
         * //时间戳这里随意使用了一个值 "nonceStr" : "adssdasssd13d", //随机串 "package" :
         * "bank_type=WX&body=XXX&fee_type=1&input_charset=GBK&notify_url=http%3a%2f
         * %2fwww.qq.com&out_trade_no=16642817866003386000&partner=1900000109&
         * spbill_create_i
         * p=127.0.0.1&total_fee=1&sign=BEEF37AD19575D92E191C1E4B1474CA9",
         * //扩展字段，由商户传入 "signType" : "SHA1", //微信签名方式:sha1 "paySign" :
         * "7717231c335a05165b1874658306fa431fe9a0de" //微信签名
         */
        private string CreateBizPackage()
        {
            var nativeObj = new Dictionary<string, string>();
            if ((bool)((CheckCftParameters())) == false)
            {
                throw new Exception("生成package参数缺失！");
            }
            nativeObj.Add("appId", _appid);
            nativeObj.Add("package", GetCftPackage());
            nativeObj.Add("timeStamp", ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString());
            nativeObj.Add("nonceStr", CommonUtil.CreateNoncestr(16));
            nativeObj.Add("paySign", GetBizSign(nativeObj));
            nativeObj.Add("signType", SignType);

            var entries = nativeObj.Select(d => string.Format("\"{0}\": \"{1}\"", d.Key, d.Value));
            return "{" + string.Join(",", entries.ToArray()) + "}";
        }

        // 生成原生支付url
        /*
         * weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX&timestamp
         * =XXXXXX&noncestr=XXXXXX
         */
        private string CreateNativeUrl(string productid)
        {
            var bizString = "";
            try
            {
                var nativeObj = new Dictionary<string, string>();
                nativeObj.Add("appid", _appid);
                nativeObj.Add("productid", WebUtility.UrlEncode(productid));
                nativeObj.Add("timestamp", ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString());
                nativeObj.Add("noncestr", CommonUtil.CreateNoncestr(16));
                nativeObj.Add("sign", GetBizSign(nativeObj));
                bizString = CommonUtil.FormatBizQueryParamMap(nativeObj, false, true);

            }
            catch (Exception e)
            {
                throw new Exception(e.Message);

            }
            return "weixin://wxpay/bizpayurl?" + bizString;
        }

        // 生成原生支付请求xml
        /*
         * <xml> <AppId><![CDATA[wwwwb4f85f3a797777]]></AppId>
         * <Package><![CDATA[a=1&url=http%3A%2F%2Fwww.qq.com]]></Package>
         * <TimeStamp> 1369745073</TimeStamp>
         * <NonceStr><![CDATA[iuytxA0cH6PyTAVISB28]]></NonceStr>
         * <RetCode>0</RetCode> <RetErrMsg><![CDATA[ok]]></ RetErrMsg>
         * <AppSignature><![CDATA[53cca9d47b883bd4a5c85a9300df3da0cb48565c]]>
         * </AppSignature> <SignMethod><![CDATA[sha1]]></ SignMethod > </xml>
         */
        private string CreateNativePackage(string retcode, string reterrmsg)
        {
            var nativeObj = new Dictionary<string, string>();
            if (CheckCftParameters() == false && retcode == "0")
            {
                throw new Exception("生成package参数缺失！");
            }
            nativeObj.Add("AppId", _appid);
            nativeObj.Add("Package", GetCftPackage());
            nativeObj.Add("TimeStamp", ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString());
            nativeObj.Add("RetCode", retcode);
            nativeObj.Add("RetErrMsg", reterrmsg);
            nativeObj.Add("NonceStr", CommonUtil.CreateNoncestr(16));
            nativeObj.Add("AppSignature", GetBizSign(nativeObj));
            nativeObj.Add("SignMethod", SignType);
            return CommonUtil.ArrayToXml(nativeObj);
        }

        /// <summary>
        /// 生成微信支付 JsApi 调用时有需要的参数
        /// </summary>
        /// <param name="tradeName">支付单名称，128 字节以下，如：广东妇保就诊挂号，广东妇保就诊缴费</param>
        /// <param name="tradeNo">商户系统内部的订单号， 32个字符内，可包含字母，确保在商户系统唯一</param>
        /// <param name="totalFee">订单总金额， 单位为分</param>
        /// <param name="clientIp">指用户浏览器端IP，不是商户服务器，格式为 IPV4</param>
        /// <param name="startTime">订单生成时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <param name="expireTime">订单失效时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <param name="notifyUrl">支付成功通知url</param>
        /// <returns></returns>
        public string GetWxJsApiPackage(string tradeName, string tradeNo, string totalFee, string clientIp, string startTime, string expireTime, string notifyUrl)
        {
            SetParameter("bank_type", "WX");
            SetParameter("body", tradeName);
            SetParameter("partner", _mchid);
            SetParameter("out_trade_no", tradeNo);
            SetParameter("total_fee", totalFee);
            //SetParameter("total_fee", "1");
            SetParameter("fee_type", "1");
            SetParameter("notify_url", notifyUrl);
            SetParameter("spbill_create_ip", String.IsNullOrEmpty(clientIp) ? "127.0.0.1" : clientIp);
            SetParameter("time_start", startTime);
            SetParameter("time_expire", expireTime);
            SetParameter("input_charset", "UTF-8");

            return CreateBizPackage();
        }

        public string CreateOrderQueryPackage(string outTradeNo)
        {
            string sign = MD5Util.GetMd5("out_trade_no=" + outTradeNo + "&partner=" + _mchid + "&key=" + _appkey);
            var nativeObj = new Dictionary<string, string>();
            nativeObj.Add("out_trade_no", outTradeNo);
            nativeObj.Add("partner", _mchid);
            nativeObj.Add("sign", sign);
            var entries = nativeObj.Select(d => string.Format("{0}={1}", d.Key, d.Value));
            return string.Join("&", entries.ToArray());
        }

        /// <summary>
        /// 生成订单查询时所需要的参数
        /// </summary>
        /// <param name="outTradeNo">微信支付外部订单号</param>
        /// <returns></returns>
        public string GetOrderQueryPackage(string outTradeNo)
        {
            SetParameter("out_trade_no", outTradeNo);
            SetParameter("partner", _mchid);

            var nativeObj = new Dictionary<string, string>();
            nativeObj.Add("appid", _appid);
            nativeObj.Add("package", GetCftPackage());
            nativeObj.Add("timestamp", ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString());
            nativeObj.Add("app_signature", GetBizSign(nativeObj));
            nativeObj.Add("sign_method", SignType);

            var entries = nativeObj.Select(d => string.Format("\"{0}\": \"{1}\"", d.Key, d.Value));
            return "{" + string.Join(",", entries.ToArray()) + "}";
        }

        /// <summary>
        /// 生成微信支付 JsApi 调用时有需要的参数
        /// </summary>
        /// <param name="tradeName">支付单名称，128 字节以下，如：广东妇保就诊挂号，广东妇保就诊缴费</param>
        /// <param name="tradeNo">商户系统内部的订单号， 32个字符内，可包含字母，确保在商户系统唯一</param>
        /// <param name="totalFee">订单总金额， 单位为分</param>
        /// <param name="clientIp">指用户浏览器端IP，不是商户服务器，格式为 IPV4</param>
        /// <param name="startTime">订单生成时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <param name="expireTime">订单失效时间，格式为 yyyyMMddHHmmss 如2009年 12 月 25 日 9点 10 分 10 秒表示为 20091225091010，时区为 GMT+8 beijing；该时间取自商户服务器；</param>
        /// <param name="notifyUrl">支付成功通知url</param>
        /// <returns></returns>
        public string GetWxAppPayPackage(string tradeName, string tradeNo, string totalFee, string clientIp, string startTime, string expireTime, string notifyUrl)
        {
            SetParameter("bank_type", "WX");
            SetParameter("body", tradeName);
            SetParameter("partner", _mchid);
            SetParameter("out_trade_no", tradeNo);
            SetParameter("total_fee", totalFee);
            //SetParameter("total_fee", "1");
            SetParameter("fee_type", "1");
            SetParameter("notify_url", notifyUrl);
            SetParameter("spbill_create_ip", String.IsNullOrEmpty(clientIp) ? "127.0.0.1" : clientIp);
            SetParameter("time_start", startTime);
            SetParameter("time_expire", expireTime);
            SetParameter("input_charset", "UTF-8");

            return GetCftPackage();
        }

    
    }
}